/*
 *  Copyright (C) 2010 Glauco
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package org.balisunrise.swing.builders;

import java.util.ArrayList;
import java.util.List;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.KeyStroke;
import org.balisunrise.annotation.EventType;
import org.balisunrise.annotation.VisualActionTarget;
import org.balisunrise.annotation.util.ActionUtilities;
import org.balisunrise.annotation.util.AnnotationsUtilitiesException;
import org.balisunrise.annotation.util.NameUtil;
import org.balisunrise.application.Application;
import org.balisunrise.builder.BuilderException;
import org.balisunrise.common.Action;
import org.balisunrise.common.FieldAction;
import org.balisunrise.common.TransactionActions;
import org.balisunrise.common.VisualAction;
import org.balisunrise.common.defaults.DefaultAction;
import org.balisunrise.common.defaults.DirectAction;
import org.balisunrise.swing.commons.DirectVisualAction;
import org.balisunrise.swing.components.ComponentFactory;

/**
 *
 * @author Glauco
 */
public class ActionBuilder implements org.balisunrise.builder.ActionBuilder{

    private static final String MESSAGE = "Erro ao construir uma Action.\n";

    @Override
    public Action createAction(ActionUtilities au) throws BuilderException {

        if(au == null){
            throw new BuilderException(MESSAGE +
                    "ActionUtilities não pode ser nulo.");
        }

        DefaultAction da = new DefaultAction();

        da.setActionMapClass(au.actionMapClass);
        da.setMethod(au.method);
        da.setWithParm(au.withParm);
        da.setProperties(au.properties);

        String name = NameUtil.buildName(au);
        da.setName(name);

        return da;
    }

    @Override
    public List<Action> createAction(Object actionMap, String alias)
            throws BuilderException {

        if(actionMap == null){
            throw new BuilderException(MESSAGE +
                    "ActionMap não pode ser nulo.");
        }

        if(alias == null){
            throw new BuilderException(MESSAGE +
                    "Alias não pode ser nulo.");
        }

        List<Action> actions = new ArrayList<Action>();

        List<ActionUtilities> list = null;
        try{
            list = ActionUtilities.createAction(actionMap.getClass());
        }catch(AnnotationsUtilitiesException ex){
            throw new BuilderException(MESSAGE,ex);
        }

        for (ActionUtilities au : list) {
            au.actionTargets = new EventType[0];
            if(au.verify(EventType.VISUAL, null, alias, null))
                actions.add(new DirectAction(actionMap, au.method, au.withParm));
        }

        return actions;
    }

    @Override
    public VisualAction createVisualAction(ActionUtilities au)
            throws BuilderException {

        if(au == null){
            throw new BuilderException(MESSAGE +
                    "ActionUtilities não pode ser nulo.");
        }

        org.balisunrise.swing.commons.VisualAction va =
                new org.balisunrise.swing.commons.VisualAction();

        va.setActionMapClass(au.actionMapClass);
        va.setMethod(au.method);
        va.setWithParm(au.withParm);
        va.setProperties(au.properties);

        String text = Application.get().getPropertiesReader()
                .processText(au.text);

        // Representação visual
        JButton button = 
                ComponentFactory.visualActionButton(au.icon, text, va);
        button.setFocusable(au.focusable);
        // Atribui Representação visual
        va.setVisualRepresentation(button);

        // nomes
        String name = NameUtil.buildName(au);
        va.setName(name);
        button.setName(name);
        button.getAccessibleContext().setAccessibleName(name);

        visualProperties(button, au);

        return va;
    }

    @Override
    public List<VisualAction> createVisualAction(Object actionMap, String alias)
                throws BuilderException {

        if(actionMap == null){
            throw new BuilderException(MESSAGE +
                    "ActionMap não pode ser nulo.");
        }

        if(alias == null){
            throw new BuilderException(MESSAGE +
                    "Alias não pode ser nulo.");
        }

        List<VisualAction> actions = new ArrayList<VisualAction>();

        List<ActionUtilities> list = null;
        try{
            list = ActionUtilities.createVisualActions(actionMap.getClass());
        }catch(AnnotationsUtilitiesException ex){
            throw new BuilderException(MESSAGE,ex);
        }

        for (ActionUtilities au : list) {
            if(au.verify(VisualActionTarget.TRANSACTION, actionMap.getClass(),
                    alias, null))
                actions.add(createDirectVisualAction(actionMap, au));
        }

        return actions;
    }

    @Override
    public VisualAction createConfirmationAction(Object actionMap, String alias)
            throws BuilderException {

        if(actionMap == null){
            throw new BuilderException(MESSAGE +
                    "ActionMap não pode ser nulo.");
        }

        if(alias == null){
            throw new BuilderException(MESSAGE +
                    "Alias não pode ser nulo.");
        }

        ActionUtilities au = null;
        try{
            au = ActionUtilities
                    .createConfirmationAction(actionMap.getClass(), alias);
        }catch(AnnotationsUtilitiesException ex){
            throw new BuilderException(MESSAGE, ex);
        }

        DirectVisualAction dva = createDirectVisualAction(actionMap, au);

        return dva;
    }

    @Override
    public VisualAction createCancellationAction(Object actionMap, String alias)
            throws BuilderException {

        if(actionMap == null){
            throw new BuilderException(MESSAGE +
                    "ActionMap não pode ser nulo.");
        }

        if(alias == null){
            throw new BuilderException(MESSAGE +
                    "Alias não pode ser nulo.");
        }

        ActionUtilities au = null;
        try{
            au = ActionUtilities
                    .createCancellationAction(actionMap.getClass(), alias);
        }catch(AnnotationsUtilitiesException ex){
            throw new BuilderException(MESSAGE, ex);
        }

        DirectVisualAction dva = createDirectVisualAction(actionMap, au);

        return dva;
    }

    @Override
    public List<VisualAction> createTransactionAction(TransactionActions ta)
            throws BuilderException {


        if(ta == null){
            throw new BuilderException(MESSAGE +
                    "TransactionActions não pode ser nulo.");
        }

        // lista de utils
        List<ActionUtilities> auList = null;

        try{
            auList = ActionUtilities.createTransactionActions();
        }catch(AnnotationsUtilitiesException ex){
            throw new BuilderException(MESSAGE, ex);
        }

        // lista de ações visuais
        List<VisualAction> list = new ArrayList<VisualAction>(4);

        for (ActionUtilities au : auList) {

            // VisualAction
            DirectVisualAction dva = createDirectVisualAction(ta, au);

            // adiciona a lista
            list.add(dva);
        }

        return list;
    }

    private DirectVisualAction createDirectVisualAction(Object actionMap,
            ActionUtilities au){

        DirectVisualAction dva =
                new DirectVisualAction(actionMap, au.method, au.withParm);

        dva.setActionMapClass(actionMap.getClass());
        dva.setProperties(au.properties);

        String text = Application.get().getPropertiesReader()
                .processText(au.text);

        // Representação visual
        JButton button =
                ComponentFactory.visualActionButton(au.icon, text, dva);
        button.setFocusable(au.focusable);
        // Atribui Representação visual
        dva.setVisualRepresentation(button);

        // nomes
        String name = NameUtil.buildName(au);
        dva.setName(name);
        button.setName(name);
        button.getAccessibleContext().setAccessibleName(name);

        visualProperties(button, au);

        // retorna
        return dva;
    }

    @Override
    public FieldAction createFieldAction(ActionUtilities au)
            throws BuilderException {

        if(au == null){
            throw new BuilderException(MESSAGE +
                    "ActionUtilities não pode ser nulo.");
        }

        Action action = createAction(au);

        org.balisunrise.swing.commons.FieldAction fa =
                new org.balisunrise.swing.commons.FieldAction(action);

        // Representação visual
        JLabel label =
                ComponentFactory.fieldActionIcon(au.icon, fa);
        // Atribui Representação visual
        fa.setVisualRepresentation(label);
        
        // nomes
        String name = NameUtil.buildName(au);
        fa.setName(name);
        label.setName(name);
        label.getAccessibleContext().setAccessibleName(name);

        return fa;
    }

    private void visualProperties(JButton button, ActionUtilities au){

        if(au.toolTipText != null && au.toolTipText.length() > 0)
            button.setToolTipText(au.toolTipText);

        if(au.accelerator != null && au.accelerator.length() > 0){
            KeyStroke stroke = KeyStroke.getKeyStroke(au.accelerator);
            String mapKey = "handler" + au.accelerator;
            button.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW)
                    .put(stroke, mapKey);
            button.getActionMap().put(mapKey, button.getAction());
        }
    }
}
